/*
 * Poliz.cpp
 *
 *  Created on: 15.04.2012
 *      Author: Eugene Kornykhin
 */

#include <vector>
#include <iostream>
#include <map>
#include <string>
#include <stdexcept>
#include "PElement.h"
#include "Poliz.h"
using namespace std;

Poliz::Poliz() {
}

Poliz::~Poliz() {
	for(vector<PElement*>::iterator it = elements.begin(); it != elements.end(); it++) {
		delete *it;
	}
}

void Poliz::append(const PElement& element) {
	elements.push_back(element.clone(*this));
}

void Poliz::saveLink(string link_name, PolizPosition link_place) {
	if (hasProcedure(link_name)) {
		throw std::logic_error("Link `" + link_name + "' is already defined as a procedure");
	} else if (hasLink(link_name)) {
		throw std::logic_error("Link `" + link_name + "' is already defined");
	} else {
		links[link_name] = link_place;
	}
}

PolizPosition Poliz::beginPosition() const {
	return PolizPosition(0);
}

PolizPosition Poliz::endPosition() const {
	return PolizPosition(numberOfElements());
}

PolizIterator Poliz::begin() {
	return PolizIterator(*this, beginPosition());
}

PolizIterator Poliz::begin() const {
	return PolizIterator(*this, beginPosition());
}

Poliz::Poliz(const Poliz& poliz)
:links(poliz.links), procedures(poliz.procedures)
{
	for(vector<PElement*>::const_iterator it = poliz.elements.begin(); it != poliz.elements.end(); it++) {
		elements.push_back((*it)->clone(*this));
	}
}

Poliz& Poliz::operator =(const Poliz& poliz) {
	links = poliz.links;
	procedures = poliz.procedures;

	for(vector<PElement*>::const_iterator it = elements.begin(); it != elements.end(); it++) {
		delete *it;
	}
	elements.clear();
	for(vector<PElement*>::const_iterator it = poliz.elements.begin(); it != poliz.elements.end(); it++) {
		elements.push_back((*it)->clone(*this));
	}
	return *this;
}

PolizIterator Poliz::end() {
	return PolizIterator(*this, endPosition());
}

void Poliz::saveProcedure(string name, PolizPosition begin)
{
	if (hasLink(name)) {
		throw std::logic_error("Link `" + name + "' is already defined as a regular link");
	} else if (hasProcedure(name)) {
		throw std::logic_error("Procedure `" + name + "' is already defined");
	} else {
		procedures[name] = begin;
	}
}

bool Poliz::hasProcedure(string proc_name) const
{
	return procedures.find(proc_name) != procedures.end();
}

bool Poliz::hasLink(string name) const
{
	return links.find(name) != links.end();
}

PolizPosition Poliz::getDestination(string link_or_proc) const
{
	if (hasProcedure(link_or_proc)) {
		return procedures.at(link_or_proc);
	} else if (hasLink(link_or_proc)) {
		return links.at(link_or_proc);
	} else {
		throw std::logic_error("Unknown link with name `" + link_or_proc + "'");
	}
}

Poliz::index_type Poliz::numberOfElements() const
{
	return elements.size();
}

PElement &Poliz::getElement(PolizPosition position) const
{
	return *elements[position.getPosition()];
}

set<string> Poliz::getLinks() const {
	set<string> keys;
	for(map<string, PolizPosition>::const_iterator it = links.begin(); it != links.end(); it++) {
		keys.insert(it->first);
	}
	return keys;
}

set<string> Poliz::getProcedures() const {
	set<string> keys;
	for(map<string, PolizPosition>::const_iterator it = procedures.begin(); it != procedures.end(); it++) {
		keys.insert(it->first);
	}
	return keys;
}
